package com.hyacinth.utils.serialize.protostuff;

import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 由于protostuff的schema中包含了对象进行序列化和反序列化的逻辑，所以构建过程比较耗时，
 * 这边需要使用缓存来做优化;
 * 缓存方案：
 * 1、ConcurrentHashMap速度快，但是不能自动对内存进行有效控制，如果元素过多，存在内存风险
 * 2、guava可以通过配置限制使用的内存大小，效率也比较快
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public class SchemaCache {

    //使用单例模式，构建缓存类
    private static class SchemaCacheHolder {
        private static SchemaCache cache = new SchemaCache();
    }

    public static SchemaCache getInstance() {
        return SchemaCacheHolder.cache;
    }

    /**
     * 使用cacheBuilder生成器生成缓存
     * maximumSize:缓存如果达到限额，则回收(接近时就会启动回收)
     * expireAfterAccess:缓存在给定时间没有被访问，则回收
     */
    private Cache<Class<?>, Schema<?>> cache = CacheBuilder.newBuilder()
            .maximumSize(1024)
            .expireAfterAccess(1, TimeUnit.HOURS)
            .build();

    /**
     * com.hyacinth.cache.get(k,Callable)方法返回缓存中的值，或者使用call方法将结果加入到缓存中
     */
    private Schema<?> get(final Class<?> clazz, Cache<Class<?>, Schema<?>> cache) {
        try {
            return cache.get(clazz, new Callable<RuntimeSchema<?>>() {
                public RuntimeSchema<?> call() throws Exception {
                    return RuntimeSchema.createFrom(clazz);
                }
            });
        } catch (ExecutionException e) {
            return null;
        }
    }

    public Schema<?> get(final Class<?> clazz) {
        return get(clazz, cache);
    }
}
